package ltd.trackan.aitextdialogue.utils;


import com.fasterxml.jackson.databind.JsonNode;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.client.WebClientResponseException;
import reactor.core.publisher.Mono;
import reactor.netty.http.client.HttpClient;

import javax.net.ssl.SSLException;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;

/**
 * webClient工具类
 *
 * @author wxa
 */
@Component
@Slf4j
public class WebClientUtils {
    private WebClientUtils() {
    }
    
    public static final String GET = "GET";
    public static final String HEAD = "HEAD";
    public static final String POST = "POST";
    public static final String PUT = "PUT";
    public static final String PATCH = "PATCH";
    public static final String DELETE = "DELETE";
    public static final String OPTIONS = "OPTIONS";
    
    private static final Map<String, String> requestHeader = new HashMap<>();
    private static final Map<String, String> requestCookie = null;
    
    static {
        requestHeader.put(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
    }
    
    /**
     * 同步 请求头 请求
     *
     * @param method      请求方法
     * @param requestPath 请求路径
     * @return 返回类型
     */
    public static JsonNode sendParams(String method, String requestPath) {
        return sendParams(method, requestPath, requestHeader, requestCookie);
    }
    
    /**
     * 同步 请求头 请求
     *
     * @param method      请求方法
     * @param requestPath 请求路径
     * @param headers     请求头
     * @return 返回类型
     */
    public static JsonNode sendParams(String method,
                                      String requestPath,
                                      Map<String, String> headers) {
        return sendParams(method, requestPath, headers, requestCookie, JsonNode.class);
    }
    
    /**
     * 同步 请求头 请求
     *
     * @param method      请求方法
     * @param requestPath 请求路径
     * @param headers     请求头
     * @return 返回类型
     */
    public static JsonNode sendParams(String method,
                                      String requestPath,
                                      Map<String, String> headers,
                                      Map<String, String> cookies) {
        return sendParams(method, requestPath, headers, cookies, JsonNode.class);
    }
    
    /**
     * 同步 请求头 请求
     *
     * @param method      请求方法
     * @param requestPath 请求路径
     * @param headers     请求头
     * @param clazz       返回来类型
     * @return 返回类型
     */
    public static <T> T sendParams(String method,
                                   String requestPath,
                                   Map<String, String> headers,
                                   Map<String, String> cookies,
                                   Class<T> clazz) {
        
        Mono<T> monoResponse = params(method, requestPath, headers, cookies)
                .exchangeToMono(response -> response.bodyToMono(clazz));
        return monoResponse.block(Duration.ofSeconds(30));
    }
    
    /**
     * 异步 请求头 请求
     *
     * @param method      请求方法
     * @param requestPath 请求路径
     * @param callBack    方法回调
     */
    public static void sendParams(String method,
                                  String requestPath,
                                  Consumer<JsonNode> callBack) {
        sendParams(method, requestPath, requestHeader, requestCookie, callBack);
    }
    
    /**
     * 异步 请求头 请求
     *
     * @param method      请求方法
     * @param requestPath 请求路径
     * @param headers     请求头
     * @param callBack    方法回调
     */
    public static void sendParams(String method,
                                  String requestPath,
                                  Map<String, String> headers,
                                  Consumer<JsonNode> callBack) {
        sendParams(method, requestPath, headers, requestCookie, callBack);
    }
    
    /**
     * 异步 请求头 请求
     *
     * @param method      请求方法
     * @param requestPath 请求路径
     * @param headers     请求头
     * @param cookies     cookies
     * @param callBack    方法回调
     */
    public static void sendParams(String method,
                                  String requestPath,
                                  Map<String, String> headers,
                                  Map<String, String> cookies,
                                  Consumer<JsonNode> callBack) {
        Mono<JsonNode> monoResponse = params(method, requestPath, headers, cookies)
                .exchangeToMono(response -> response.bodyToMono(JsonNode.class));
        monoResponse.subscribe(callBack);
    }
    
    /**
     * 同步 body 请求
     *
     * @param method        请求方法
     * @param requestPath   请求路径
     * @param requestParams 请求参数
     * @return JsonNode类型
     */
    public static JsonNode sendBody(String method,
                                    String requestPath,
                                    Object requestParams) {
        
        return sendBody(method, requestPath, requestParams, requestHeader, requestCookie);
    }
    
    /**
     * 同步 body 请求
     *
     * @param method        请求方法
     * @param requestPath   请求路径
     * @param requestParams 请求参数
     * @param headers       请求头
     * @return JsonNode类型
     */
    public static JsonNode sendBody(String method,
                                    String requestPath,
                                    Object requestParams,
                                    Map<String, String> headers) {
        return sendBody(method, requestPath, requestParams, headers, requestCookie, JsonNode.class);
    }
    
    /**
     * 同步 body 请求
     *
     * @param method        请求方法
     * @param requestPath   请求路径
     * @param requestParams 请求参数
     * @param headers       请求头
     * @param cookies       cookies
     * @return JsonNode类型
     */
    public static JsonNode sendBody(String method,
                                    String requestPath,
                                    Object requestParams,
                                    Map<String, String> headers,
                                    Map<String, String> cookies) {
        return sendBody(method, requestPath, requestParams, headers, cookies, JsonNode.class);
    }
    
    /**
     * 同步 body 请求
     *
     * @param method        请求方法
     * @param requestPath   请求路径
     * @param requestParams 请求参数
     * @param headers       请求头
     * @param clazz         返回来类型
     * @return JsonNode类型
     */
    public static <T> T sendBody(String method,
                                 String requestPath,
                                 Object requestParams,
                                 Map<String, String> headers,
                                 Map<String, String> cookies,
                                 Class<T> clazz) {
        
        Mono<T> monoResponse = body(method, requestPath, headers, cookies)
                .body(BodyInserters.fromValue(requestParams))
                .exchangeToMono(response -> response.bodyToMono(clazz));
        return monoResponse.block(Duration.ofSeconds(30));
    }
    
    /**
     * 异步 body 请求
     *
     * @param method        请求方法
     * @param requestPath   请求路径
     * @param requestParams 请求参数
     * @param callBack      方法回调
     */
    public static void sendBody(String method,
                                String requestPath,
                                Object requestParams,
                                Consumer<JsonNode> callBack) {
        sendBody(method, requestPath, requestParams, requestHeader, requestCookie, callBack);
    }
    
    /**
     * 异步 body 请求
     *
     * @param method        请求方法
     * @param requestPath   请求路径
     * @param requestParams 请求参数
     * @param headers       请求头
     * @param callBack      方法回调
     */
    public static void sendBody(String method,
                                String requestPath,
                                Object requestParams,
                                Map<String, String> headers,
                                Consumer<JsonNode> callBack) {
        sendBody(method, requestPath, requestParams, headers, requestCookie, callBack);
    }
    
    /**
     * 异步 body 请求
     *
     * @param method        请求方法
     * @param requestPath   请求路径
     * @param requestParams 请求参数
     * @param headers       请求头
     * @param callBack      方法回调
     */
    public static void sendBody(String method,
                                String requestPath,
                                Object requestParams,
                                Map<String, String> headers,
                                Map<String, String> cookies,
                                Consumer<JsonNode> callBack) {
        Mono<JsonNode> monoResponse = body(method, requestPath, headers, cookies)
                .body(BodyInserters.fromValue(requestParams))
                .exchangeToMono(response -> response.bodyToMono(JsonNode.class));
        monoResponse.subscribe(callBack);
    }
    
    
    /**
     * body 校验
     *
     * @param method      方法
     * @param requestPath 请求路径
     */
    public static WebClient.RequestBodySpec body(String method,
                                                 String requestPath) {
        return body(method, requestPath, requestHeader, requestCookie);
    }
    
    /**
     * body 校验
     *
     * @param method      方法
     * @param requestPath 请求路径
     * @param headers     请求头
     */
    public static WebClient.RequestBodySpec body(String method,
                                                 String requestPath,
                                                 Map<String, String> headers,
                                                 Map<String, String> cookies) {
        WebClient.RequestBodySpec requestBodySpec;
        switch (method) {
            case POST:
                requestBodySpec = createIgnoreSslWebClient().post().uri(requestPath);
                break;
            case PUT:
                requestBodySpec = createIgnoreSslWebClient().put().uri(requestPath);
                break;
            case PATCH:
                requestBodySpec = createIgnoreSslWebClient().patch().uri(requestPath);
                break;
            default:
                throw new IllegalArgumentException("请求方法错误");
        }
        if (headers != null) {
            headers.forEach(requestBodySpec::header);
        }
        if (cookies != null) {
            cookies.forEach(requestBodySpec::cookie);
        }
        return requestBodySpec;
    }
    
    /**
     * params 校验
     *
     * @param method      方法
     * @param requestPath 请求路径
     */
    public static WebClient.RequestHeadersSpec<?> params(String method, String requestPath) {
        return params(method, requestPath, requestHeader, requestCookie);
    }
    
    /**
     * params 校验
     *
     * @param method      方法
     * @param requestPath 请求路径
     * @param headers     请求头
     */
    public static WebClient.RequestHeadersSpec<?> params(String method,
                                                         String requestPath,
                                                         Map<String, String> headers,
                                                         Map<String, String> cookies) {
        WebClient.RequestHeadersSpec<?> requestBodySpec;
        switch (method) {
            case GET:
                requestBodySpec = createIgnoreSslWebClient().get().uri(requestPath);
                break;
            case HEAD:
                requestBodySpec = createIgnoreSslWebClient().head().uri(requestPath);
                break;
            case DELETE:
                requestBodySpec = createIgnoreSslWebClient().delete().uri(requestPath);
                break;
            case OPTIONS:
                requestBodySpec = createIgnoreSslWebClient().options().uri(requestPath);
                break;
            default:
                throw new IllegalArgumentException("请求方法错误");
        }
        if (headers != null) {
            headers.forEach(requestBodySpec::header);
        }
        if (cookies != null) {
            cookies.forEach(requestBodySpec::cookie);
        }
        return requestBodySpec;
    }
    
    /**
     * 创建web客户端，免除ssl协议验证
     */
    public static WebClient createIgnoreSslWebClient() {
        try {
            SslContext sslContext = SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build();
            HttpClient httpClient = HttpClient.create().secure(t -> t.sslContext(sslContext));
            return WebClient.builder()
                    // 免除ssl协议验证
                    .clientConnector(new ReactorClientHttpConnector(httpClient))
                    .build();
        } catch (SSLException sslException) {
            throw new IllegalArgumentException(sslException);
        }
    }
    
    /**
     * 流式请求方法
     *
     * @param requestPath 请求路径
     * @param headers     请求头
     * @param body        请求体
     * @param handler     流式数据处理器
     */
    public static void sendStream(String requestPath,
                                  Map<String, String> headers,
                                  Object body,
                                  Consumer<String> handler) {
        // 创建忽略 SSL 验证的 WebClient 实例
        WebClient client = createIgnoreSslWebClient();
        
        // 发起 POST 请求
        client.post()
                // 设置请求路径
                .uri(requestPath)
                // 添加请求头
                .headers(httpHeaders -> headers.forEach(httpHeaders::add))
                // 设置请求体
                .body(BodyInserters.fromValue(body))
                // 获取响应
                .retrieve()
                // 将响应作为流式数据处理
                .bodyToFlux(String.class)
                /// 处理每个分片
                .doOnError(WebClientResponseException.class, ex -> {
                    log.error("请求失败: {}", ex.getMessage());
                    String errorMessage = "{\"choices\":[{\"index\":0,\"delta\":{\"content\":\"\"},\"logprobs\":null,\"finish_reason\":\"stop\",\"message\": \""+ ex.getMessage()+"\"}]}";
                    handler.accept(errorMessage);
                })
                .subscribe(handler);
    }
}